<template>
  <div class="TOC-container">
    <h2>目录</h2>
    <RightList :list="tocWithSelect" @select="handleSelect" />
  </div>
</template>

<script>
import RightList from "./RightList.vue";
import {debunce} from '@/utils'
export default {
  data() {
    return {
      activeAnchor: "",
    };
  },
  props: {
    toc: {
      type: Array,
    },
  },
  created() {
    this.seleDebunce = debunce(this.setSelect,50); // 添加防抖 防止刷新频率过高影响运行机制
    this.$bus.$on("mainscroll",this.seleDebunce); //监听mainscroll事件 让setSelect处理
  },
  // 移除事件总件监听
  destroyed(){
    this.$bus.$off("mainscroll",this.seleDebunce); //监听mainscroll事件 让setSelect处理
  },
  computed: {
    tocWithSelect() {
      /**
       *        递归思路  先拿到原先的toc数据 0 {anchor: "article-md-title-1"name: "概述"}
            数组map方法 遍历一个旧的数组 返回一个新的数组
            然后添加一个新的属性 isSelect 因为有些数组有children子元素 所以还要重新调用一下gettoc(t.children)
            有些没有 防止报错 则设置默认值为空数组[]
       */
      const getToc = (toc = []) => {
        return toc.map((t) => ({
          // map遍历 得到一个新的对象
          ...t, //展开旧的toc数据
          isSelect: t.anchor === this.activeAnchor, // 给新的对象添加isSlect属性
          children: getToc(t.children), // children可能是undfinde 使用给一个默认的空数组
        }));
      };
      return getToc(this.toc);
    },
    // 根据toc得到它们对应的元素数组
    doms() {
      /**
       * t.anchor 在dom元素中 有类样式id名为t.anchor article-md-title-1
       * 获取toc数据 进行对象遍历
       * 然后将获取到的数据添加到doms空数组里
       */
      const doms = [];
      const addToDoms = (toc) => {
        for (const t of toc) {
          doms.push(document.getElementById(t.anchor));
          if (t.children && t.children.length) {
            addToDoms(t.children);
          }
        }
      };
      addToDoms(this.toc);
      return doms;
    },
  },
  components: {
    RightList,
  },
  methods: {
    handleSelect(item) {
      //点击改变hash值 跳转到对应数据信息
      location.hash = item.anchor;
    },
    // 设置activeAnchor为正确的值
    setSelect() {
      this.activeAnchor = ""; // 因为后续要重新设置 先清空之前的状态
      const range = 200; //定义一个目标状态值 以range为基准
      for (const dom of this.doms) {
        if (!dom) {
          // 判断当前dom元素是否应该被选中
          continue;
        }
        const top = dom.getBoundingClientRect().top; // 获取视口到顶部的距离
        if (top >= 0 && top <= range) {
          // 在规定的范围内
          this.activeAnchor = dom.id;
          return;
        } else if (top > range) {
          // 在规定的范围下方
          return;
        } else {
          // 在规定的范围上方
          this.activeAnchor = dom.id; //先预设自己是激活状态 在继续看后面
        }
      }
    },
  },
};
</script>

<style lang="less" scoped>
@import url("~@/style/var.less");
.TOC-container {
  width: 100%;
  box-sizing: border-box;
  padding: 20px;
  scroll-behavior: smooth;
  h2 {
    font-weight: bold;
    letter-spacing: 2px;
    font-size: 1em;
    margin: 0;
  }
}
</style>